#include "AvatarLibrary.h"
#include <CEngine.h>
#include <CStringUtil.h>
#include <CZipFile.h>
#include <CLog.h>
#include <QDebug>
#include <QDir>
#include <QFile>
#ifdef Q_OS_WIN
#include <Windows.h>
#elif defined(Q_OS_LINUX)
#include <dlfcn.h>
#endif

static CEngine* engine = 0;
std::function<void(const QString&, const QString&)> logcallback = nullptr;
void log_callback(const char* header, const char* content) { if (logcallback) logcallback(QString(header), QString(content)); }

void AvatarLibrary::avatar_init(const QString& directory, void* hwnd)
{
    if (engine) {
        engine->Destroy();
        delete engine;
        engine = 0;
    }
    SEngineConfig conf;
    conf.directory = directory.toStdString();
    conf.graphic.title = CStringUtil::Format("$%ld", reinterpret_cast<uint64_t>(hwnd));
    conf.graphic.antialias = true;
    conf.log.enable = true;
    conf.log.console = false;
    conf.log.file = false;
    conf.log.level = 1;
    CEngine* temp = new CEngine();
    if (!temp->Init(conf)) {
        qDebug() << "初始化引擎失败";
        delete temp;
    } else engine = temp;
}

void AvatarLibrary::avatar_log(int level, std::function<void(const QString&, const QString&)> callback)
{
    logcallback = callback;
    CLog::SetLevel((CLog::Level)level);
    CLog::SetCallback((CLog::LogCallback)log_callback);
}

void AvatarLibrary::avatar_resize(int width, int height)
{
    if (engine) {
        CEngine::GetInputManager()->Resize(width, height);
    }
}

void AvatarLibrary::avatar_mouse(int x, int y, int button, int delta)
{
    if (engine) {
        CEngine::GetInputManager()->MouseInput(x, y, button, delta);
    }
}

void AvatarLibrary::avatar_keyboard(int key)
{
    if (engine) {
        CEngine::GetInputManager()->KeyboardInput(key);
    }
}

void AvatarLibrary::avatar_move(float x, float y, float z)
{
    if (engine) {
        float dt = engine->GetTimeSpan();
        CEngine::GetInputManager()->Move(x * dt, y * dt, z * dt);
    }
}

void AvatarLibrary::avatar_update()
{
    if (engine) {
        engine->Update();
        engine->Render();
    }
}

void AvatarLibrary::avatar_command(const QString& command)
{
    if (engine) {
        if (!command.isEmpty()) qDebug() << command;
        CEngine::GetScriptManager()->Script(command.toUtf8().data(), true);
    }
}

void AvatarLibrary::avatar_camera_position(QVector3D& pos)
{
    if (engine) {
        CCamera* camera = CEngine::GetGraphicsManager()->GetCamera();
        pos[0] = camera->m_cPosition[0];
        pos[1] = camera->m_cPosition[1];
        pos[2] = camera->m_cPosition[2];
    }
}

void AvatarLibrary::avatar_camera_angle(QVector3D& pos)
{
    if (engine) {
        CCamera* camera = CEngine::GetGraphicsManager()->GetCamera();
        camera->GetAngle(&pos[0], &pos[1], &pos[2]);
    }
}

void AvatarLibrary::avatar_camera_target(QVector3D& pos)
{
    if (engine) {
        CCamera* camera = CEngine::GetGraphicsManager()->GetCamera();
        camera->GetTarget(&pos[0], &pos[1], &pos[2]);
    }
}

void AvatarLibrary::avatar_camera_orientation(QVector4D& pos)
{
    if (engine) {
        const CMatrix4& view = CEngine::GetGraphicsManager()->GetCamera()->GetViewMatrix();
        CMatrix4 rotation;
        rotation[0] = view(0, 0);
        rotation[1] = -view(2, 0);
        rotation[2] = view(1, 0);
        rotation[4] = view(0, 1);
        rotation[5] = -view(2, 1);
        rotation[6] = view(1, 1);
        rotation[8] = view(0, 2);
        rotation[9] = -view(2, 2);
        rotation[10] = view(1, 2);
        CQuaternion orient = CQuaternion(rotation, true).Conjugate();
        pos[0] = orient[0];
        pos[1] = orient[1];
        pos[2] = orient[2];
        pos[3] = orient[3];
    }
}

void AvatarLibrary::avatar_picking(int sx, int sy, QVector3D& pos)
{
    if (engine) {
        CVector3 output;
        CEngine::GetGraphicsManager()->PickingPosition(sx, sy, output);
        pos[0] = output[0];
        pos[1] = output[1];
        pos[2] = output[2];
    }
}

void AvatarLibrary::avatar_register(const QString& function, int(*callback)(int, const char**, void*), void* userdata)
{
    if (engine) {
        CEngine::GetScriptManager()->Register(function.toStdString(), callback, userdata);
    }
}

void AvatarLibrary::avatar_execute(const QString& script, QString& result)
{
    if (engine) {
        result = QString::fromUtf8(CEngine::GetScriptManager()->Script(script.toUtf8().data(), false));
    }
}

void AvatarLibrary::avatar_exit()
{
    if (engine) {
        engine->Destroy();
        delete engine;
        engine = 0;
    }
}

void AvatarLibrary::avatar_document(QString& document)
{
#ifdef Q_OS_WIN
    HMODULE hMODULE = GetModuleHandle(TEXT("Avatar.dll"));
    HRSRC hRSRC = FindResource(hMODULE, MAKEINTRESOURCE(101), RT_RCDATA);
    HGLOBAL hGLOBAL = LoadResource(hMODULE, hRSRC);
    LPVOID data = LockResource(hGLOBAL);
    int length = (int)SizeofResource(hMODULE, hRSRC);
    document.append(QString::fromUtf8((const char*)data, length));
#elif defined(Q_OS_LINUX)
    void* handle = dlopen("libavatar.so", RTLD_NOLOAD);
    char* data = (char*)dlsym(handle, "_binary_document_Doxyfile_h_start");
    size_t length = (size_t)dlsym(handle, "_binary_document_Doxyfile_h_size");
    document.append(QString::fromUtf8(data, length));
#endif
}

void AvatarLibrary::avatar_compress(const QString& base, const QStringList& files, const QString& zip)
{
    QDir dir(base);
    CZipFile zipWriter(CZipFile::OpenType::WRITE, zip.toUtf8().data());
    foreach (QString file, files) {
        QFile file_(dir.filePath(file));
        if (!file_.open(QIODevice::ReadOnly)) continue;
        QByteArray byteArray = file_.readAll();
        file_.close();
        zipWriter.Open(file.toUtf8().data());
        zipWriter.Write((unsigned char*)byteArray.data(), byteArray.size());
        zipWriter.Close();
    }
}
